package com.bbb.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bbb.pojo.User;
import com.bbb.pojo.UserLike;
import com.bbb.pojo.UserWorks;
import com.bbb.service.RecommendationService;
import com.bbb.service.UserLikeService;
import com.bbb.service.UserService;
import com.bbb.service.UserWorksService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class RecommendationServiceImpl implements RecommendationService {

    @Autowired
    private UserLikeService userLikeService;

    @Autowired
    private UserWorksService userWorksService;

    @Autowired
    private UserService userService;

    // 计算用户之间的余弦相似度
    private double cosineSimilarity(Map<Integer, Integer> user1Likes, Map<Integer, Integer> user2Likes) {
        Set<Integer> allWorks = new HashSet<>(user1Likes.keySet());
        allWorks.addAll(user2Likes.keySet());

        double dotProduct = 0;
        double magnitude1 = 0;
        double magnitude2 = 0;
        for (Integer workId : allWorks) {
            int value1 = user1Likes.getOrDefault(workId, 0);
            int value2 = user2Likes.getOrDefault(workId, 0);
            dotProduct += value1 * value2;
            magnitude1 += Math.pow(value1, 2);
            magnitude2 += Math.pow(value2, 2);
        }
        magnitude1 = Math.sqrt(magnitude1);
        magnitude2 = Math.sqrt(magnitude2);
        if (magnitude1 * magnitude2 == 0) {
            return 0;
        }
        return dotProduct / (magnitude1 * magnitude2);
    }

    // 获取用户的喜欢作品列表
    private Map<Integer, Integer> getUserLikes(Integer userId) {
        List<UserLike> userLikes = userLikeService.list(new LambdaQueryWrapper<UserLike>().eq(UserLike::getUserId, userId));
        return userLikes.stream()
                .collect(Collectors.toMap(UserLike::getId, UserLike::getUserId, (a, b) -> a));
    }

    // 获取用户关注的作者列表
    private Set<Integer> getUserFollowedAuthors(Integer userId) {
        List<UserLike> userLikes = userLikeService.list(new LambdaQueryWrapper<UserLike>().eq(UserLike::getUserId, userId));
        return userLikes.stream()
                .map(UserLike::getAuthorId)
                .collect(Collectors.toSet());
    }

    // 获取作品的点赞热度
    private Map<Integer, Integer> getWorksPopularity() {
        List<UserLike> allLikes = userLikeService.list();
        return allLikes.stream()
                .collect(Collectors.groupingBy(UserLike::getId, Collectors.summingInt(like -> 1)));
    }

    // 为用户生成推荐列表
    @Override
    public List<UserWorks> generateRecommendations(Integer userId) {
        User user = userService.getById(userId);
        Map<Integer, Integer> userLikes = getUserLikes(userId);
        Set<Integer> followedAuthors = getUserFollowedAuthors(userId);
        Map<Integer, Integer> worksPopularity = getWorksPopularity();

        // 冷启动：如果用户没有喜欢的作品，根据作品热度推荐
        if (userLikes.isEmpty()) {
            return getPopularWorks(worksPopularity);
        }

        // 存储用户之间的相似度
        Map<Integer, Double> userSimilarities = new HashMap<>();
        // 获取所有用户
        List<User> allUsers = userService.list();
        for (User otherUser : allUsers) {
            if (!otherUser.getId().equals(userId)) {
                Map<Integer, Integer> otherUserLikes = getUserLikes(otherUser.getId());
                double similarity = cosineSimilarity(userLikes, otherUserLikes);
                userSimilarities.put(otherUser.getId(), similarity);
            }
        }
        // 按相似度排序
        userSimilarities = userSimilarities.entrySet().stream()
                .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

        // 找到相似用户喜欢的作品
        Map<Integer, Double> recommendedWorks = new HashMap<>();
        for (Map.Entry<Integer, Double> entry : userSimilarities.entrySet()) {
            Integer similarUserId = entry.getKey();
            double similarity = entry.getValue();
            Map<Integer, Integer> similarUserLikes = getUserLikes(similarUserId);
            for (Integer workId : similarUserLikes.keySet()) {
                if (!userLikes.containsKey(workId)) {
                    double score = similarity * worksPopularity.getOrDefault(workId, 1);
                    recommendedWorks.put(workId, recommendedWorks.getOrDefault(workId, 0.0) + score);
                }
            }
        }

        // 优先推荐关注作者的作品
        for (Integer authorId : followedAuthors) {
            List<UserWorks> authorWorks = userWorksService.list(new LambdaQueryWrapper<UserWorks>().eq(UserWorks::getUserId, authorId));
            for (UserWorks work : authorWorks) {
                if (!userLikes.containsKey(work.getId())) {
                    recommendedWorks.put(work.getId(), recommendedWorks.getOrDefault(work.getId(), 0.0) + 10); // 给予较高的权重
                }
            }
        }

        // 按推荐得分排序
        recommendedWorks = recommendedWorks.entrySet().stream()
                .sorted(Map.Entry.<Integer, Double>comparingByValue(Comparator.reverseOrder()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));

        // 获取推荐作品列表
        List<Integer> recommendedWorkIds = new ArrayList<>(recommendedWorks.keySet());
        List<UserWorks> recommendedWorkList = userWorksService.listByIds(recommendedWorkIds);

        // 获取所有作品列表
        List<UserWorks> allWorks = userWorksService.list();

        // 合并推荐作品和所有作品，去除重复项
        Set<Integer> recommendedWorkIdSet = recommendedWorkIds.stream().collect(Collectors.toSet());
        List<UserWorks> combinedWorks = new ArrayList<>(recommendedWorkList);
        for (UserWorks work : allWorks) {
            if (!recommendedWorkIdSet.contains(work.getId())) {
                combinedWorks.add(work);
            }
        }

        return combinedWorks;
    }

    // 根据作品热度获取推荐作品
    private List<UserWorks> getPopularWorks(Map<Integer, Integer> worksPopularity) {
        List<Map.Entry<Integer, Integer>> sortedWorks = new ArrayList<>(worksPopularity.entrySet());
        sortedWorks.sort(Map.Entry.<Integer, Integer>comparingByValue(Comparator.reverseOrder()));
        List<Integer> popularWorkIds = sortedWorks.stream()
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
        return userWorksService.listByIds(popularWorkIds);
    }
}